package com.david.mydemo.util;

import com.google.common.base.Strings;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.io.*;
import java.util.Properties;
import java.util.Vector;


@Slf4j
public class SftpClientUtil {

    /**
     * 超时时间
     */
    private final int TIMEOUT = 60 * 1000;

    //    private static String host = "132.98.23.90";
//    private static String username = "agsserver";
//    private static String password = "arcgis@1234";
    private String host = "";
    private String username = "";
    private String password = "";
    private static int port = 22;

    protected String privateKey;// 密钥文件路径
    protected String passphrase;// 密钥口令

    private static SftpClientUtil sftpClient = null;
    private ChannelSftp sftp = null;
    private Session sshSession = null;
    private Object CREATE_PATH_LOCK = new Object();

    public static SftpClientUtil getInstance(String host, String username, String password) {
        if (sftpClient == null) {
            sftpClient = new SftpClientUtil(host, username, password, port);
        }
        return sftpClient;
    }

    public static SftpClientUtil getInstance(String host, String username, String password, int port) {
        if (sftpClient == null) {
            sftpClient = new SftpClientUtil(host, username, password, port);
        }
        return sftpClient;
    }

    private SftpClientUtil() {

    }

    private SftpClientUtil(String host, String username, String password, int port) {
        this.host = host;
        this.username = username;
        this.password = password;
        this.port = port;
    }

    private SftpClientUtil(String host, String username, String password, int port, String privateKey, String passphrase) {
        this.host = host;
        this.username = username;
        this.password = password;
        this.privateKey = privateKey;
        this.passphrase = passphrase;
        this.port = port;
    }

    /**
     * 下载单个文件
     *
     * @param remotePath     下载目录
     * @param remoteFileName 下载文件名
     * @param localPath      本地保存目录(以路径符号结束)
     * @param localFileName  保存文件名
     * @return
     */
    public synchronized String downloadFile(String remotePath, String remoteFileName, String localPath, String localFileName) {
        log.info(remotePath + "/" + remoteFileName + "/" + localPath + "/" + localFileName);
        try {
            if (sftp == null || !isConnected()) {
                connect();
            }
            File filePath = new File(localPath);
            if (!filePath.exists()) {
                filePath.mkdirs();
            }
            File file = new File(localPath + localFileName);
            sftp.cd(remotePath);
            sftp.get(remoteFileName, new FileOutputStream(file));
            return localPath + localFileName;
        } catch (FileNotFoundException e) {
            log.error("不存在文件,Path:" + remotePath + ",file:" + remoteFileName, e);
        } catch (SftpException e) {
            log.error("下载文件处理异常,Path:" + remotePath + ",file:" + remoteFileName, e);
        } finally {
            disconnect();
        }
        return null;
    }

    /**
     * 上传单个文件
     *
     * @param remotePath     远程保存目录
     * @param remoteFileName 保存文件名
     * @param localPath      本地上传目录(以路径符号结束)
     * @param localFileName  上传的文件名
     * @return
     */
    public String uploadFile(String remotePath, String remoteFileName, String localPath, String localFileName) {
        File fileInput = new File(localPath + localFileName);
        FileInputStream fis = null;
        try {
            if (sftp == null) {
                connect();
            }
            fis = new FileInputStream(fileInput);
            String currentPath = mkDirWithPath(remotePath.replace(sftp.pwd(), ""));// 绝对路径变为相对路径
            sftp.cd(currentPath);
            sftp.put(fis, remoteFileName);
            return remotePath + remoteFileName;
        } catch (Exception e) {
            log.error("上传单个文件异常", e);
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    log.warn("关闭sftp资源异常", e);
                }
            }
        }
        return null;
    }

    /***
     * ls列出所有的文件列表
     * @param remotePath
     * @return
     */
    public synchronized Vector<ChannelSftp.LsEntry> listFiles(String remotePath) {
        try {
            if (sftp == null)
                connect();
            if (sftp == null)
                return null;
            Vector<ChannelSftp.LsEntry> vector = sftp.ls(remotePath);
            return vector;
        } catch (SftpException e) {
            log.error("ls列出所有文件列表出错，文件目录：" + remotePath, e);
        } finally {
//            disconnect();
        }
        return null;
    }

    /**
     * 读取文件大小
     *
     * @param remotePath     路径
     * @param remoteFileName 文件名
     * @return
     */
    public Long getTargetFileSize(String remotePath, String remoteFileName) {
        log.info(remotePath + "/" + remoteFileName);
        Long fileSize = 0L;
        try {
            if (sftp == null || !isConnected()) {
                connect();
            }
            sftp.cd(remotePath);
            Vector<ChannelSftp.LsEntry> ls = listFiles(remotePath);
            for (ChannelSftp.LsEntry lsEntry : ls) {
                String fileName = lsEntry.getFilename();
                Long size = lsEntry.getAttrs().getSize();
//                log.info("fileName: " + fileName + "，size: " + size);
                if (remoteFileName.equals(fileName)) {
                    fileSize = size;
                    break;
                }
            }
        } catch (SftpException e) {
            log.error("读取文件大小处理异常,Path:" + remotePath + ",file:" + remoteFileName, e);
        } finally {
            disconnect();
        }
        return fileSize;
    }

    public String deleteFile(String remotePath, String remoteFile) {
        try {
            if (sftp == null)
                connect();
            if (openDir(remotePath)) {
                sftp.rm(remoteFile);
            }
            return remotePath + remoteFile;
        } catch (SftpException e) {
            log.error("删除文件【" + remotePath + "/" + remoteFile + "】发生异常！", e);
            return null;
        }
    }

    public void connect() {
        JSch jsch = new JSch();
        Channel channel = null;
        try {
            if (!StringUtils.isEmpty(privateKey)) {
                // 使用密钥验证方式，密钥可以使有口令的密钥，也可以是没有口令的密钥
                if (!StringUtils.isEmpty(passphrase)) {
                    jsch.addIdentity(privateKey, passphrase);
                } else {
                    jsch.addIdentity(privateKey);
                }
            }
            sshSession = jsch.getSession(username, host, port);
            if (!StringUtils.isEmpty(password)) {
                sshSession.setPassword(password);
            }
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");// do not verify host key
            sshSession.setConfig(sshConfig);
            sshSession.setTimeout(TIMEOUT);
            // session.setServerAliveInterval(92000);
            sshSession.connect();
            // 参数sftp指明要打开的连接是sftp连接
            channel = sshSession.openChannel("sftp");
            channel.connect();
            sftp = (ChannelSftp) channel;
        } catch (JSchException e) {
            log.error("连接【" + host + ":" + port + "】异常", e);
        }
    }

    /**
     * 关闭资源
     */
    public void disconnect() {
        if (sftp != null) {
            if (sftp.isConnected()) {
                sftp.disconnect();
            }
        }
        if (sshSession != null) {
            if (sshSession.isConnected()) {
                sshSession.disconnect();
            }
        }
    }

    /**
     * 销毁当前实例
     */
    public void destory() {
        sftp = null;
        sshSession = null;
        sftpClient = null;
    }

    /**
     * sftp is connected
     *
     * @return
     */
    public boolean isConnected() {
        return sftp != null && sftp.isConnected();
    }

    /**
     * 创建目录
     *
     * @param createpath
     * @return
     */
    public boolean createDir(String createpath) {
        try {
            if (isDirExist(createpath)) {
                this.sftp.cd(createpath);
                return true;
            }
            String[] pathArry = createpath.split("/");
            StringBuffer filePath = new StringBuffer("/");
            for (String path : pathArry) {
                if (path.equals("")) {
                    continue;
                }
                filePath.append(path + "/");
                if (isDirExist(filePath.toString())) {
                    sftp.cd(filePath.toString());
                } else {
                    // 建立目录
                    sftp.mkdir(filePath.toString());
                    // 进入并设置为当前目录
                    sftp.cd(filePath.toString());
                }
            }
            return true;
        } catch (SftpException e) {
            log.error("sftp创建目录异常", e);
        }
        return false;
    }

    /**
     * 判断目录是否存在
     *
     * @param directory
     * @return
     */
    public boolean isDirExist(String directory) {
        try {
            SftpATTRS sftpATTRS = sftp.lstat(directory);
            return sftpATTRS.isDir();
        } catch (Exception e) {
            log.error("sftp.lstat()目录isDirExist异常", e);
            return false;
        }
    }

    /**
     * 通过路径创建目录
     * <p>通过路径创建好目录</p>
     *
     * @param path
     */
    public String mkDirWithPath(String path) {
        String[] dirs = path.split("/");
        try {
            String currentPath = sftp.pwd();
            for (int i = 0; i < dirs.length; i++) {
                if (Strings.isNullOrEmpty(dirs[i]))
                    continue;
                if (!openDir(dirs[i])) {
                    sftp.mkdir(dirs[i]);
                    sftp.cd(dirs[i]);
                }
            }
            // 当前目录
            return currentPath + "/" + path;
        } catch (SftpException e) {
            log.error("通过路径创建目录 Exception : " + e);
            return null;
        }
    }

    /**
     * 打开文件夹
     *
     * @param dir
     * @return
     */
    public boolean openDir(String dir) {
        try {
            log.debug("opendir: {}", dir);
            sftp.cd(dir);
            return true;
        } catch (SftpException e) {
            log.debug("openDir【" + dir + "】 Exception : " + e);
            return false;
        }
    }

    public OutputStream getUpLoadStream(String path, String name) throws Exception {
        if (sftp == null || !isConnected()) {
            connect();
        }
        synchronized (CREATE_PATH_LOCK) {
            if (!openDir(path)) {
                createDir(path);
            }
        }
        sftp.cd(path);
        return sftp.put(name);
    }


    public static void main(String[] args) {

//        Long fileSize = SftpClientUtil.getInstance(host, username, password).getTargetFileSize(remotePath, removeFileName);
//        log.info("fileSize(B): " + fileSize);
//        fileSize = new BigDecimal(fileSize).divide(new BigDecimal("1024"), RoundingMode.HALF_UP).longValue();
//        log.info("fileSize(KB): " + fileSize);
//        double fileSizeMb = new BigDecimal(fileSize).divide(new BigDecimal("1024"), 2, RoundingMode.HALF_UP).doubleValue();
//        log.info("fileSize(MB): " + fileSizeMb);


    }


}
